import json
from datetime import datetime

from django.conf import settings
from django.core.paginator import Paginator, EmptyPage
from django.http import HttpResponseNotFound, JsonResponse, HttpResponseForbidden
from django.shortcuts import render
from django_redis import get_redis_connection
from django.views import View
from redis.client import Pipeline

from apps.contents.utils import get_categories, get_breadcrumb
from utils import constants
from utils.cookiesecret import CookieSecret
from utils.response_code import RETCODE
from .models import GoodsCategory, SKU, GoodsVisitCount


class ListView(View):
    """商品列表页"""
    def get(self, request, category_id, page_num):
        # 校验category_id是否有效
        try:
            category = GoodsCategory.objects.get(id=category_id)
        except GoodsCategory.DoesNotExist as e:
            settings.LOGGER.error("查询分类不存在", e)
            return HttpResponseNotFound('无效的分类')
        # 1 三级分类目录
        categories = get_categories()

        # 2 面包屑导航
        breadcrumb = get_breadcrumb(category)

        # 3 商品分页数据
        # 3.1 获取排序字段 默认上架时间default='creat_time'  price='price'  hot='-sales'
        sort = request.GET.get('sort', 'default')
        sort_field_table = {"default": "-create_time", "price": "price", "hot": "-sales"}
        sort_field = sort_field_table.get(sort)
        # 3.2 获取当前分类下的商品
        skus = category.sku_set.filter(is_launched=True, category_id=category_id).order_by(sort_field)

        # 3.3 创建分页器,每页N条数据
        paginator = Paginator(skus, constants.GOODS_LIST_LIMIT)
        # 3.4 获取分页数据, 需要考虑分页超出页数范围
        try:
            page_skus = paginator.page(page_num)
        except EmptyPage:
            # 如果page_num不正确，默认给用户404
            return HttpResponseNotFound('empty page')

        context = {
            "categories": categories,
            "breadcrumb": breadcrumb,
            "category_id": category_id,
            "skus": page_skus,
            "cur_page": page_num,
            "totalPage": paginator.num_pages,
            "sort": sort
        }

        return render(request, 'list.html', context)


# 提供热销展示数据
class HotView(View):

    def get(self, request, category_id):
        """
        hot_skus[id	SKU编号
        default_image_url	商品默认图片
        name	商品名称
        price ]	热销SKU列表

        """
        # print(category_id)
        try:
            hot_skus = SKU.objects.filter(is_launched=True, category_id=category_id).order_by('-sales')[:3]
            hot_skus = [{"id": sku.id, "default_image_url": sku.default_image.url, "name":sku.name, "price": sku.price} for sku in hot_skus]
        except SKU.DoesNotExist as e:
            settings.LOGGER.error("热销品类不存在", e)
            return JsonResponse({"code": RETCODE.DBERR, "errmsg": "品类不存在", "hot_skus": []})
        return JsonResponse({"code": RETCODE.OK, "errmsg": "0", "hot_skus": hot_skus})


# 商品详情页
class DetailView(View):

    def get(self, request, sku_id):
        # 1 三级分类目录
        categories = get_categories()

        # 2 面包屑导航
        try:
            sku = SKU.objects.get(is_launched=True, id=sku_id)
        except SKU.DoesNotExist:
            return render(request, '404.html')
        breadcrumb = get_breadcrumb(sku.category)

        # 构建当前商品的规格键
        sku_specs = sku.specs.order_by('spec_id')
        sku_key = []
        for spec in sku_specs:
            sku_key.append(spec.option.id)
        # 获取当前商品的所有SKU
        skus = sku.spu.sku_set.all()
        # 构建不同规格参数（选项）的sku字典
        spec_sku_map = {}
        for s in skus:
            # 获取sku的规格参数
            s_specs = s.specs.order_by('spec_id')
            # 用于形成规格参数-sku字典的键
            key = []
            for spec in s_specs:
                key.append(spec.option.id)
            # 向规格参数-sku字典添加记录
            spec_sku_map[tuple(key)] = s.id
        # 获取当前商品的规格信息
        goods_specs = sku.spu.specs.order_by('id')
        # 若当前sku的规格信息不完整，则不再继续
        if len(sku_key) < len(goods_specs):
            return
        for index, spec in enumerate(goods_specs):
            # 复制当前sku的规格键
            key = sku_key[:]
            # 该规格的选项
            spec_options = spec.options.all()
            for option in spec_options:
                # 在规格参数sku字典中查找符合当前规格的sku
                key[index] = option.id
                option.sku_id = spec_sku_map.get(tuple(key))
            spec.spec_options = spec_options

        context = {
            "categories": categories,
            "breadcrumb": breadcrumb,
            "sku": sku,
            'specs': goods_specs,
        }
        return render(request, 'detail.html', context)


# 详情页评价展示
class CommentsView(View):
    def get(self, request, sku_id):
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except:
            return JsonResponse({"code": RETCODE.USERERR, "errmsg": "商品不存在"})
        comments = sku.ordergoods_set.filter(is_commented=True).order_by('-create_time')
        comment_list = [{
            'user_photo_url': '',
            'username': (comment.order.user.username[0] + '***' + comment.order.user.username[-1]) if comment.is_anonymous else comment.order.user.username,
            'comment': comment.comment,
            'score': str(comment.score),
        } for comment in comments]
        return JsonResponse({"comment_list": comment_list})


# 统计分类商品日访问量
class CategoryGoodsCountView(View):

    def post(self, request, category_id):
        try:
            category = GoodsCategory.objects.get(id=category_id)
        except GoodsCategory.DoesNotExist as e:
            settings.LOGGER.error(e)
            return JsonResponse({"code": RETCODE.DBERR, "errmsg": "分类不存在"})

        date_str = datetime.now().strftime("%Y-%m-%d")
        date = datetime.strptime(date_str, "%Y-%m-%d")

        try:
            cur_visit_count = category.goodsvisitcount_set.get(date=date)
        except GoodsVisitCount.DoesNotExist as e:
            GoodsVisitCount.objects.create(category_id=category_id, count=1, date=date)
        else:
            cur_visit_count.count += 1
            cur_visit_count.save()
        return JsonResponse({"code": RETCODE.OK, "errmsg": "0"})


# 购物车
class CartsView(View):
    # 展示购物车
    def get(self, request):
        user = request.user
        cart_skus = []
        if user.is_anonymous:
            carts_str = request.COOKIES.get("carts")
            if carts_str:
                carts_dict = CookieSecret.loads(carts_str)
                skus = SKU.objects.filter(id__in=carts_dict.keys())

                cart_skus = [{
                    'id': sku.id,
                    'name': sku.name,
                    'count': carts_dict[sku.id][0],
                    'selected': str(carts_dict[sku.id][1]),
                    'default_image_url': sku.default_image.url,
                    'price': str(sku.price),
                    'amount': str(carts_dict[sku.id][0] * sku.price),
                } for sku in skus]
        else:
            # 注意从redis中取出的数据类型为bytes
            carts_cli = get_redis_connection('carts')
            redis_dict = carts_cli.hgetall(f"carts_{user.id}")
            selected_skus = carts_cli.smembers(f"carts_selected_{user.id}")

            # 构建字典{sku_id: [count, is_selected]}
            carts_dict = {}
            for sku_id, count in redis_dict.items():
                carts_dict[int(sku_id)] = [int(count), sku_id in selected_skus]

            skus = SKU.objects.filter(id__in=carts_dict.keys())

            # 构建前端数据
            cart_skus = [{
                'id': sku.id,
                'name': sku.name,
                'count': carts_dict[sku.id][0],
                'selected': str(carts_dict[sku.id][1]),
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),
                'amount': str(carts_dict[sku.id][0] * sku.price),
            } for sku in skus]

        context = {
            'cart_skus': cart_skus
        }

        return render(request, 'cart.html', context)

    # 添加购物车
    def post(self, request):
        """添加购物车"""
        json_dict = json.loads(request.body)
        sku_id = json_dict.get('sku_id')	         # int	是	商品SKU编号
        count = json_dict.get('count')	             # int	是	商品数量
        selected = json_dict.get('selected', True)	 # bool	否	是否勾选

        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
            count = int(count)
            assert isinstance(selected, bool), 0 < count < 6
            # sku_id = str(sku_id)
        except Exception as e:
            settings.LOGGER.error('添加购物车参数错误', e)
            return HttpResponseForbidden('添加购物车参数错误')

        # 判断是否登陆
        user = request.user
        cur_response = JsonResponse({'code': RETCODE.OK, 'errmsg': '添加购物车成功'})
        if user.is_anonymous:
            # 未登录,取cookie
            # carts_userID:{sku_id:[count, is_selected(bool)]}
            carts_str = request.COOKIES.get("carts", {})
            if carts_str:
                carts_str = CookieSecret.loads(carts_str)
            carts_sku_lst = carts_str.setdefault(sku_id, [0, True])
            count = carts_sku_lst[0] + count
            if not 0 < count < 6:
                return JsonResponse({'code': RETCODE.USERERR, 'errmsg': '数量超出限制'})
            carts_sku_lst[0] = count
            # 加密
            carts_str = CookieSecret.dumps(carts_str)
            cur_response.set_cookie(key="carts", value=carts_str, max_age=14*24*3600)

        else:
            # 登陆,取redis
            carts_cli = get_redis_connection('carts')
            count += int(carts_cli.hget(f"carts_{user.id}", sku_id) or 0)
            if not 0 < count < 6:
                return JsonResponse({'code': RETCODE.USERERR, 'errmsg': '数量超出限制'})

            # carts_userID:{sku_id:count}  carts_selected_userID:{sku_id, sku_id}
            p_carts_cli = carts_cli.pipeline()

            p_carts_cli.hset(f"carts_{user.id}", sku_id, count)
            p_carts_cli.sadd(f"carts_selected_{user.id}", sku_id)
            p_carts_cli.execute()

        return cur_response

    # 修改购物车
    def put(self, request):
        """添加购物车"""
        json_dict = json.loads(request.body)
        sku_id = json_dict.get('sku_id')	         # int	是	商品SKU编号
        count = json_dict.get('count')	             # int	是	商品数量
        selected = json_dict.get('selected', True)	 # bool	否	是否勾选

        # 校验参数
        try:
            sku = SKU.objects.get(id=sku_id)
            count = int(count)
            assert isinstance(selected, bool), 0 < count < 6
            # sku_id = str(sku_id)
        except Exception as e:
            settings.LOGGER.error('添加购物车参数错误', e)
            return HttpResponseForbidden('添加购物车参数错误')

        # 判断是否登陆
        user = request.user

        if user.is_anonymous:
            # 未登录,取cookie
            # carts_userID:{sku_id:[count, is_selected(bool)]}
            carts_str = request.COOKIES.get("carts")
            if carts_str:
                carts_str = CookieSecret.loads(carts_str)
                carts_str[sku_id] = [count, selected]

            # 加密
            carts_str = CookieSecret.dumps(carts_str)
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': count * sku.price,
            }
            cur_response = JsonResponse({'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})
            cur_response.set_cookie(key="carts", value=carts_str, max_age=14*24*3600)
            return cur_response
        else:
            # 登陆,取redis
            carts_cli = get_redis_connection('carts')

            # carts_userID:{sku_id:count}  carts_selected_userID:{sku_id, sku_id}
            p_carts_cli = carts_cli.pipeline()

            p_carts_cli.hset(f"carts_{user.id}", sku_id, count)
            p_carts_cli.sadd(f"carts_selected_{user.id}", sku_id) if selected else p_carts_cli.srem(f"carts_selected_{user.id}", sku_id)
            p_carts_cli.execute()
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': count * sku.price,
            }
            return JsonResponse({'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})

    # 删除购物车
    def delete(self, request):
        """添加购物车"""
        json_dict = json.loads(request.body)
        sku_id = json_dict.get('sku_id')	         # int	是	商品SKU编号

        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except Exception as e:
            settings.LOGGER.error('删除购物车参数错误', e)
            return HttpResponseForbidden('删除购物车参数错误')

        # 判断是否登陆
        user = request.user
        cur_response = JsonResponse({'code': RETCODE.OK, 'errmsg': '删除成功'})

        if user.is_anonymous:
            # 未登录,取cookie
            # carts_userID:{sku_id:[count, is_selected(bool)]}
            carts_str = request.COOKIES.get("carts")
            if carts_str:
                carts_str = CookieSecret.loads(carts_str)
                del carts_str[sku_id]

            # 加密
            carts_str = CookieSecret.dumps(carts_str)

            cur_response.set_cookie(key="carts", value=carts_str, max_age=14*24*3600)
        else:
            # 登陆,取redis
            carts_cli = get_redis_connection('carts')

            # carts_userID:{sku_id:count}  carts_selected_userID:{sku_id, sku_id}
            p_carts_cli = carts_cli.pipeline()

            p_carts_cli.hdel(f"carts_{user.id}", sku_id)
            p_carts_cli.srem(f"carts_selected_{user.id}", sku_id)
            p_carts_cli.execute()

        return cur_response


class CartsSelectAllView(View):
    # 全部选中
    def put(self, request):
        """添加购物车"""
        json_dict = json.loads(request.body)
        selected = json_dict.get('selected')	 # bool	必传	是否勾选

        # 校验参数
        try:
            assert isinstance(selected, bool)
        except Exception as e:
            settings.LOGGER.error('购物车全选参数错误', e)
            return HttpResponseForbidden('购物车全选参数错误')

        # 判断是否登陆
        user = request.user
        cur_response = JsonResponse({'code': RETCODE.OK, 'errmsg': '全选购物车成功'})

        if user.is_anonymous:
            # 未登录,取cookie
            # carts_userID:{sku_id:[count, is_selected(bool)]}
            carts_str = request.COOKIES.get("carts")
            if carts_str:
                carts_str = CookieSecret.loads(carts_str)
                for values in carts_str.values():
                    values[1] = selected

            # 加密
            carts_str = CookieSecret.dumps(carts_str)

            cur_response.set_cookie(key="carts", value=carts_str, max_age=14*24*3600)
        else:
            # 登陆,取redis
            carts_cli = get_redis_connection('carts')

            # carts_userID:{sku_id:count}  carts_selected_userID:{sku_id, sku_id}
            sku_id_lst = carts_cli.hkeys(f"carts_{user.id}")
            if selected:
                carts_cli.sadd(f"carts_selected_{user.id}", *sku_id_lst)
            else:
                carts_cli.srem(f"carts_selected_{user.id}", *sku_id_lst)

        return cur_response


# 购物车简单展示
class CartsSimpleView(View):
    def get(self, request):
        # code 状态码
        # errmsg 错误信息
        cart_skus = []
        user = request.user
        if user.is_anonymous:
            cookie_carts = request.COOKIES.get('carts')
            if cookie_carts:
                carts_dict = CookieSecret.loads(cookie_carts)
                skus = SKU.objects.filter(id__in=carts_dict.keys())
                cart_skus = [{
                    "id": sku.id,
                    "name": sku.name,
                    "count": carts_dict.get(sku.id)[0],
                    "default_image_url": sku.default_image.url,
                }for sku in skus]
        else:
            carts_redis_cli:Pipeline = get_redis_connection('carts')
            carts_dict = carts_redis_cli.hgetall(f"carts_{user.id}")
            skus = SKU.objects.filter(id__in=carts_dict.keys())
            cart_skus = [{
                "id": sku.id,
                "name": sku.name,
                "count": int(carts_dict[str(sku.id).encode()]),
                "default_image_url": sku.default_image.url,
            }for sku in skus]

        return JsonResponse({"code": RETCODE.OK, "errmsg":"ok", "cart_skus": cart_skus})
